﻿<h1>类型转换、赋值和值比较规则大全</h1>

<p>
此篇文章将列出Go中所有的类型转换、赋值和值比较规则。
</p>

<a class="anchor" id="conversion"></a>
<h3>类型转换规则大全</h3>

<!--
<p>
注意：《Go语言101》中<b>类型转换</b>术语的意思和Go白皮书中并不完全一致。
Go白皮书中的类型转换只包括显式类型转换，但是《Go语言101》中类型转换包括显式和隐式转换。
</p>
-->

<p>
在Go中，如果一个值<code>v</code>可以被显式地转换为类型<code>T</code>，则此转换可以使用语法形式<code>(T)(v)</code>来表示。
在大多数情况下，特别是<code>T</code>为一个类型名（即一个标识符）时，此形式可简化为<code>T(v)</code>。
</p>

<p>
当我们说一个值<code>x</code>可以被隐式转换为一个类型<code>T</code>，这同时也意味着<code>x</code>可以被显式转换为类型<code>T</code>。
</p>

<h4>1. 显然的类型转换规则</h4>

<div class="well">
如果两个类型表示着同一个类型，则它们的值可以相互<u><b>隐式</b></u>转换为这两个类型中的任意一个。
</div>

<div>
比如，
<ul>
<li>
	类型<code>byte</code>和<code>uint8</code>的任何值可以转换为这两个类型中的任意一个。
</li>
<li>
	类型<code>rune</code>和<code>int32</code>的任何值可以转换为这两个类型中的任意一个。
</li>
<li>
	类型<code>[]byte</code>和<code>[]uint8</code>的任何值可以转换为这两个类型中的任意一个。
</li>
</ul>
</div>

<p>
此条规则没什么可解释的，无论你是否认为此种情况中发生了转换。
</p>

<a class="anchor" id="conversion-rule-underlying-types"></a>
<h4>2. 底层类型相关的类型转换规则</h4>

<div class="well">
给定一个非接口值<code>x</code>和一个非接口类型<code>T</code>，并假设<code>x</code>的类型为<code>Tx</code>，
<ul>
<li>
	如果类型<code>Tx</code>和<code>T</code>的<a href="type-system-overview.html#underlying-type">底层类型</a>相同（忽略掉结构体字段标签），则<code>x</code>可以被显式转换为类型<code>T</code>。
</li>
<li>
	如果类型<code>Tx</code>和<code>T</code>中至少有一个是<a href="type-system-overview.html#non-defined-type">非定义类型</a>并且它们的底层类型相同（考虑结构体字段标签），则<code>x</code>可以被<u><b>隐式</b></u>转换为类型<code>T</code>。
</li>
<li>
	如果类型<code>Tx</code>和<code>T</code>的底层类型不同，但是两者都是非定义的指针类型并且它们的基类型的底层类型相同（忽略掉结构体字段标签），则<code>x</code>可以（而且只能）被显式转换为类型<code>T</code>。
</li>
</ul>
</div>

<p><i>
（注意：两处“忽略掉结构体字段标签”从Go 1.8开始生效。）
</i></p>

<div>
一个例子：
<pre class="line-numbers"><code class="language-go">package main

func main() {
	// 类型[]int、IntSlice和MySlice共享底层类型：[]int。
	type IntSlice []int
	type MySlice  []int

	var s  = []int{}
	var is = IntSlice{}
	var ms = MySlice{}
	var x struct{n int `foo`}
	var y struct{n int `bar`}

	// 这两行隐式转换编译不通过。
	/*
	is = ms
	ms = is
	*/

	// 必须使用显式转换。
	is = IntSlice(ms)
	ms = MySlice(is)
	x = struct{n int `foo`}(y)
	y = struct{n int `bar`}(x)

	// 这些隐式转换是没问题的。
	s = is
	is = s
	s = ms
	ms = s
}
</code></pre>

<p>
</p>

指针相关的转换例子：
<pre class="line-numbers"><code class="language-go">package main

func main() {
	type MyInt int
	type IntPtr *int
	type MyIntPtr *MyInt

	var pi = new(int)  // pi的类型为*int
	var ip IntPtr = pi // 没问题，因为底层类型相同
	                   // 并且pi的类型为非定义类型。

	// var _ *MyInt = pi // 不能隐式转换
	var _ = (*MyInt)(pi) // 显式转换是没问题的

	// 类型*int的值不能被直接转换为类型MyIntPtr，
	// 但是可以间接地转换过去。
	/*
	var _ MyIntPtr = pi  // 不能隐式转换
	var _ = MyIntPtr(pi) // 也不能显式转换
	*/
	var _ MyIntPtr = (*MyInt)(pi)  // 间接隐式转换没问题
	var _ = MyIntPtr((*MyInt)(pi)) // 间接显式转换没问题

	// 类型IntPtr的值不能被直接转换为类型MyIntPtr，
	// 但是可以间接地转换过去。
	/*
	var _ MyIntPtr = ip  // 不能隐式转换
	var _ = MyIntPtr(ip) // 也不能显式转换
	*/
	// 间接隐式或者显式转换都是没问题的。
	var _ MyIntPtr = (*MyInt)((*int)(ip))  // ok
	var _ = MyIntPtr((*MyInt)((*int)(ip))) // ok
}
</code></pre>
</div>

<h4>3. 通道相关的类型转换规则</h4>

<div class="well">
给定一个通道值<code>x</code>，假设它的类型<code>Tx</code>是一个双向通道类型，<code>T</code>也是一个通道类型（无论是双向的还是单向的）。如果<code>Tx</code>和<code>T</code>的元素类型相同并且它们中至少有一个为非定义类型，则<code>x</code>可以被<u><b>隐式</b></u>转换为类型<code>T</code>。
</div>

<!--
a potentail bug: https://github.com/golang/go/issues/29469
-->

<div>
一个例子：
<pre class="line-numbers"><code class="language-go">package main

func main() {
	type C chan string
	type C1 chan<- string
	type C2 <-chan string

	var ca C
	var cb chan string

	cb = ca // ok，因为底层类型相同
	ca = cb // ok，因为底层类型相同

	// 这4行都满足此第3条转换规则的条件。
	var _, _ chan<- string = ca, cb // ok
	var _, _ <-chan string = ca, cb // ok
	var _ C1 = cb                   // ok
	var _ C2 = cb                   // ok

	// 类型C的值不能直接转换为类型C1或C2。
	/*
	var _ = C1(ca) // compile error
	var _ = C2(ca) // compile error
	*/

	// 但是类型C的值可以间接转换为类型C1或C2。
	var _ = C1((chan<- string)(ca)) // ok
	var _ = C2((<-chan string)(ca)) // ok
	var _ C1 = (chan<- string)(ca)  // ok
	var _ C2 = (<-chan string)(ca)  // ok
}
</code></pre>
</div>

<a class="anchor" id="conversion-rule-implementation"></a>
<h4>4. 和接口实现相关的类型转换规则</h4>

<div class="well">
给定一个值<code>x</code>和一个接口类型<code>I</code>，如果<code>x</code>的类型（或者默认类型）为<code>Tx</code>并且类型<code>Tx</code>实现了接口类型<code>I</code>，则<code>x</code>可以被<u><b>隐式</b></u>转换为类型<code>I</code>。
此转换的结果为一个类型为<code>I</code>的接口值。此接口值包裹了
<ul>
<li><code>x</code>的一个副本（如果<code>Tx</code>是一个非接口值）；</li>
<li><code>x</code>的动态值的一个副本（如果<code>Tx</code>是一个接口值）。</li>
</ul>
</div>

<!--
<div>
一个例子：
<pre class="line-numbers"><code class="language-go">package main

type Filter interface {
	Filte(n int) bool
}

type MultipleFilter struct {
	Divisor int
}

func (mf MultipleFilter) Filte(n int) bool {
	return n % mf.Divisor == 0
}

func main() {
	var mf = MultipleFilter{11}
	// mf的类型MultipleFilter实现了接口类型Filter，
	// 所以下面这个隐式转换是没问题的。
	var filter = mf

	// 任何类型都实现了空接口类型interface{}，所以任何
	// 值都可以被隐式转换为类型interface{}。

	var n int = 123
	var b bool = true
	var s string = "abc"

	var _ interface{} = n
	var _ interface{} = b
	var _ interface{} = s
	var _ interface{} = mf

	// 接口类型Filter也实现了空接口类型interface{}。
	var _ interface{} = filter
}
</code></pre>
<p>
</p>
</div>

<a class="anchor" id="conversion-rule-type-assertion"></a>
<h4>4. 类型断言中的类型转换规则</h4>
-->

<div class="well">
给定一个动态类型为<code>T</code>的接口值<code>x</code>，<code>x</code>可以通过类型断言的方式<code>x.(T)</code>安全地转换为类型<code>T</code>。
</div>

<div class="well">
给定一个接口值<code>x</code>和一个接口类型<code>I</code>，如果<code>x</code>的动态类型实现了接口类型<code>I</code>，则<code>x</code>可以通过类型断言的方式<code>x.(I)</code>安全地转换为接口类型<code>I</code>。
</div>

<!--
<div>
一个例子：
<pre class="line-numbers"><code class="language-go">package main

type I interface {
	f()
}

type T string
func (T) f(){}

func main() {
	var it interface{} = T("abc")
	var is interface{} = "abc"

	// 这两个断言既编译没问题，也不会导致恐慌，因为
	// 类型T同时实现了接口类型I和interface{}。
	var _ T = it.(T)
	var _ I = it.(I)

	// 这个断言也没问题，因为内置类型string实现了interface{}。
	var _ string = is.(string)

	// 下面这三行均编译没问题，但是都将在运行时刻产生一个恐慌。
	// _ = is.(I)
	// _ = is.(T)
	// _ = it.(string)
}
</code></pre>
</div>
-->

<p>
请阅读<a href="interface.html">接口</a>一文获取更多详情和示例。
</p>

<h4>5. 类型不确定值相关的类型转换规则</h4>

<div class="well">
如果一个类型不确定值可以表示为类型<code>T</code>的值，则它可以被<u><b>隐式</b></u>转换为类型<code>T</code>。
</div>

<div>
一个例子：
<pre class="line-numbers"><code class="language-go">package main

func main() {
	var _ []int = nil
	var _ map[string]int = nil
	var _ chan string = nil
	var _ func()() = nil
	var _ *bool = nil
	var _ interface{} = nil

	var _ int = 123.0
	var _ float64 = 123
	var _ int32 = 1.23e2
	var _ int8 = 1 + 0i
}
</code></pre>
</div>

<h4>6. 常量相关的类型转换规则</h4>

<p><i>
（此规则和上一条规则有些重叠。）
</i></p>

<p>
常量的类型转换结果一般仍然是一个常量。（除了下面第8条规则中将介绍的字符串转换为字节切片或者码点切片的情况。）
</p>

<div class="well">
给定一个常量值<code>x</code>和一个类型<code>T</code>，如果<code>x</code>可以表示成类型<code>T</code>的一个值，则<code>x</code>可以被显式地转换为类型<code>T</code>；特别地，如果<code>x</code>是一个类型不确定值，则它可以被<u><b>隐式</b></u>转换为类型<code>T</code>。
</div>

<!--
<p>
Note, in case of <code>x</code> is a floating-point constant and <code>T</code> is a floating-point type,
the conversion result <code>T(x)</code> is the rounded value of <code>x</code> by using IEEE 754 round-to-even rules,
but with an IEEE <code>-0.0</code> further rounded to an unsigned <code>0.0</code>.
</p>
-->

<div>
一个例子：
<pre class="line-numbers"><code class="language-go">package main

func main() {
	const I = 123
	const I1, I2 int8 = 0x7F, -0x80
	const I3, I4 int8 = I, 0.0

	const F = 0.123456789
	const F32 float32 = F
	const F32b float32 = I
	const F64 float64 = F
	const F64b = float64(I3) // 这里必须显式转换

	const C1, C2 complex64 = F, I
	const I5 = int(C2) // 这里必须显式转换
}
</code></pre>
</div>

<h4>7. 非常量数值转换规则</h4>

<div class="well">
非常量浮点数和整数值可以被显式转换为任何浮点数和整数类型。
</div>

<div class="well">
非常量复数值可以被显式转换为任何复数类型。
</div>

<div>
注意，
<ul>
<li>非常量复数值不能被转换为浮点数或整数类型。</li>
<li>非常量浮点数和整数值不能被转换为复数类型。</li>
<li>在非常量数值的转换过程中，溢出和舍入是允许的。当一个浮点数被转换为整数时，小数部分将被舍弃（向零靠拢）。</li>
</ul>

一个例子：
<pre class="line-numbers"><code class="language-go">package main

import "fmt"

func main() {
	var a, b = 1.6, -1.6 // 类型均为float64
	fmt.Println(int(a), int(b)) // 1 -1

	var i, j int16 = 0x7FFF, -0x8000
	fmt.Println(int8(i), uint16(j)) // -1 32768

	var c1 complex64 = 1 + 2i
	var _ = complex128(c1)
}
</code></pre>
</div>

<h4>8. 字符串相关的转换规则</h4>

<div class="well">
如果一个值的类型（或者默认类型）为一个整数类型，则此值可以被当作一个码点值（rune值）显式转换为任何字符串类型。
</div>

<div class="well">
一个字符串可以被显式转换为一个字节切片类型，反之亦然。
字节切片类型是指底层类型为<code>[]byte</code>的类型。
</div>

<div class="well">
一个字符串可以被显式转换为一个码点切片类型，反之亦然。
码点切片类型是指底层类型为<code>[]rune</code>的类型。
</div>

<p>
请阅读<a href="string.html#conversions">字符串</a>一文获取更多详情和示例。
</p>

<h4>9. 非类型安全指针相关的类型转换规则</h4>

<p>
非类型安全指针类型是指底层类型为<code>unsafe.Pointer</code>的类型。
</p>

<div class="well">
任何类型安全指针类型的值可以被显式转化为一个非类型安全指针类型，反之亦然。
</div>

<div class="well">
任何uintptr值可以被显式转化为一个非类型安全指针类型，反之亦然。
</div>

<p>
请阅读<a href="unsafe.html">非类型安全指针</a>一文获取详情和示例。
</p>

<h3>赋值规则</h3>

<p>
赋值可以看作是隐式类型转换。
各种隐式转换规则在上一节中已经列出。
</p>

<p>
除了这些规则，赋值语句中的目标值必须为一个可寻址的值、一个映射元素表达式或者一个空标识符。
</p>

<!--
<div class="well">
In an assignment, if the target value is a new declared variable without given a
type in code, then the type of the new variable is
<ul>
<li>the type of the source value, if the source value is a typed value;</li>
<li>the default type of the source value, if the source value is an untyped constant.</li>
</ul>
</div>

<p>
</p>
-->

<p>
在一个赋值中，源值被复制给了目标值。精确地说，源值的<a href="value-part.html">直接部分</a>被复制给了目标值。
</p>

<p>
注意：函数传参和结果返回其实都是赋值。
</p>

<!--
<p>
For most value assignments, the <a href="value-part.html">direct part</a>
of each source value is copied to the direct part of the corresponding
destination value, except that destination value is an interface value.
</p>

<p>
In an assignment, if the destination value is an interface value but
the source value is not, then (the direct part of) the source value
will be copied and the copy result will be stored in
the destination interface value as dynamic value.
The standard compiler makes some special treatments here
if the source value is not a pointer value.
If the source value is not a pointer value,
the direct part of the source value
will be copied and a pointer to the (hidden and immutable) copy
will be stored in the destination interface value.
</p>

<p>
In an assignment,
if both the destination and source values are interface values,
then (the direct part of) the dynamic value of the source interface value
will be copied and the copy result will be stored in
the destination interface value as dynamic value.
The standard compiler makes some optimizations here so that
a pointer is always copied in the process.
</p>
-->

<a class="anchor" id="comparison-rules"></a>
<h3>值比较规则</h3>

<div>

<p>
Go白皮书<a href="https://golang.google.cn/ref/spec#Comparison_operators">提到</a>：
</p>

<div class="alert alert-success">
在任何比较中，第一个比较值必须能被赋值给第二个比较值的类型，或者反之。
</div>

<p>
所以，值比较规则和赋值规则非常相似。
换句话说，两个值是否可以比较取决于其中一个值是否可以隐式转换为另一个值的类型。
很简单？此规则描述基本正确，但是有一个例外：
</p>

<!--
https://github.com/golang/go/issues/28164#issuecomment-430733364
-->
<div class="alert alert-danger">
如果两个值中有一个为接口值，而另一个为非接口值并且它的类型为一个<a href="type-system-overview.html#types-not-support-comparison">不可比较类型</a>，则这两个值不可比较，即使此非接口值可以被隐式转换为接口值的类型（即此不可比较类型实现了接口值的类型）。
</div>

<p>
注意，尽管切片/映射/函数类型为不可比较类型，但是它们的值可以和类型不确定的预声明<code>nil</code>标识符比较。
</p>

上述规则并未覆盖所有的情况。如果两个值均为类型不确定值，它们可以比较吗？这种情况的规则比较简单：
<ul>
<li>
	两个类型不确定的布尔值可以相互比较。
</li>
<li>
	两个类型不确定的数字值可以相互比较。
</li>
<li>
	两个类型不确定的字符串值可以相互比较。
</li>
</ul>

<p>
两个类型不确定的数字值的比较结果服从直觉。
</p>

<p>
注意，两个类型不确定的nil值不能相互比较。
</p>

<p>
任何比较的结果均为一个类型不确定的布尔值。
</p>

一些值比较的例子：

<pre class="line-numbers"><code class="language-go">package main

// 一些类型为不可比较类型的变量。
var s []int
var m map[int]int
var f func()()
var t struct {x []int}
var a [5]map[int]int

func main() {
	// 这些比较编译不通过。
	/*
	_ = s == s
	_ = m == m
	_ = f == f
	_ = t == t
	_ = a == a
	_ = nil == nil
	_ = s == interface{}(nil)
	_ = m == interface{}(nil)
	_ = f == interface{}(nil)
	*/

	// 这些比较编译都没问题。
	_ = s == nil
	_ = m == nil
	_ = f == nil
	_ = 123 == interface{}(nil)
	_ = true == interface{}(nil)
	_ = "abc" == interface{}(nil)
}
</code></pre>

<p>
</p>

</div>

<a class="anchor" id="comparison-implementation"></a>
<h4>两个值是如何进行比较的？</h4>

<div>
假设两个值可以相互比较，并且它们的类型同为<code>T</code>。
（如果它们的类型不同，则其中一个可以转换为另一个的类型。这里我们不考虑两者均为类型不确定值的情形。）
<ol>
<li>
	如果<code>T</code>是一个布尔类型，则这两个值只有在它们同为<code>true</code>或者<code>false</code>的时候比较结果才为<code>true</code>。
</li>
<li>
	如果<code>T</code>是一个整数类型，则这两个值只有在它们在内存中的表示完全一致的情况下比较结果才为<code>true</code>。
</li>
<li>
	如果<code>T</code>是一个浮点数类型， 则这两个值只要满足下面任何一种情况，它们的比较结果就为<code>true</code>：
	<ul>
	<li>
		它们都为<code>+Inf</code>；
	</li>
	<li>
		它们都为<code>-Inf</code>；
	</li>
	<li>
		它们都为<code>-0.0</code>或者都为<code>+0.0</code>。
	</li>
	<li>
		它们都不是<code>NaN</code>并且它们在内存中的表示完全一致。
	</li>
	</ul>
</li>
<li>
	如果<code>T</code>是一个复数类型，则这两个值只有在它们的实部和虚部均做为浮点数进行进行比较的结果都为<code>true</code>的情况下比较结果才为<code>true</code>。
</li>
<li>
	如果<code>T</code>是一个指针类型（类型安全或者非类型安全），则这两个值只有在它们所表示的地址值相等或者它们都为nil的情况下比较结果才为<code>true</code>。
</li>
<li>
	如果<code>T</code>是一个通道类型，则这两个值只有在它们引用着相同的底层内部通道或者它们都为nil时比较结果才为<code>true</code>。
</li>
<li>
	如果<code>T</code>是一个结构体类型，则<a href="struct.html#comparison">它们的相应字段将逐对进行比较</a>。只要有一对字段不相等，这两个结构体值就不相等。
</li>
<li>
	如果<code>T</code>是一个数组类型，则<a href="container.html#comparison">它们的相应元素将逐对进行比较</a>。只要有一对元素不相等，这两个结构体值就不相等。
</li>
<li>
	如果<code>T</code>是一个接口类型，请参阅<a href="interface.html#comparison">两个接口值是如何进行比较的</a>。
</li>
<li>
	如果<code>T</code>是一个字符串类型，请参阅<a href="string.html#comparison">两个字符串值是如何进行比较的</a>。
</li>
</ol>


请注意，动态类型均为同一个不可比较类型的两个接口值的比较将产生一个恐慌。比如下面的例子：

<pre class="line-numbers"><code class="language-go">package main

func main() {
	type T struct {
		a interface{}
		b int
	}
	var x interface{} = []int{}
	var y = T{a: x}
	var z = [3]T{{a: y}}

	// 这三个比较均会产生一个恐慌。
	_ = x == x
	_ = y == y
	_ = z == z
}
</code></pre>

<p>
</p>
</div>


